// tomo-fft.cpp : Defines the entry point for the console application.
//
#include "windows.h"
#include "process.h"

#include "stdafx.h"
#include "TomoFFT.h"
#include "DataTransManager.h"
#include "Timer.h"
#include "errorCheck.h"
#include <float.h>

#define VERBOSE 0
#define VERIFY 1

void generateTestData(float2* pData, unsigned int size, unsigned int batchSize) 
{
    for (unsigned int i = 0; i < size; i++)
    {
        pData->x = (rand()/(float)RAND_MAX)*2-1;
        pData->y = (rand()/(float)RAND_MAX)*2-1;
        //pData->x = (float)(i % batchSize);
        //pData->y = 0;
        pData++;
    }
}

void generateTestData(float* pData, unsigned int size, unsigned int batchSize) 
{
    for (unsigned int i = 0; i < size; i++)
    {
        //pData->x = (rand()/(float)RAND_MAX)*2-1;
        //pData->y = (rand()/(float)RAND_MAX)*2-1;
        *pData = (float)(i % batchSize);
        pData++;
    }
}


inline double2 operator*( double2 a, double2 b ) { return double2( a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x ); }
inline double2 operator+( double2 a, double2 b ) { return double2( a.x + b.x, a.y + b.y ); }
inline double2 operator-( double2 a, double2 b ) { return double2( a.x - b.x, a.y - b.y ); }
#define M_PI 3.14159265358979323846

inline float2 operator*( float2 a, float2 b ) { return float2( a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x ); }


#if VERIFY=1

void computeFFTReference( float2 *dst, float2 *src, int n, int batch )
{   
    double2 *X = new double2[n];
    double2 *Y = new double2[n];
    for( int ibatch = 0; ibatch < batch; ibatch++ )
    {
        // go to double precision
        for( int i = 0; i < n; i++ )
            X[i] = double2( src[i].x, src[i].y );

        // FFT in double precision
        for( int kmax = 1, jmax = n/2; kmax < n; kmax *= 2, jmax /= 2 )
        {
            for( int k = 0; k < kmax; k++ )
            {
                double phi = -2.0 * M_PI * k /(2.0 * kmax);
                double2 w = double2( cos(phi), sin(phi) ); 
                for( int j = 0; j < jmax; j++ )
                {
                    Y[j*2*kmax + k]        = X[j*kmax + k] + w * X[j*kmax + n/2 + k];
                    Y[j*2*kmax + kmax + k] = X[j*kmax + k] - w * X[j*kmax + n/2 + k];
                }
            }
            double2 *T = X;
            X = Y;
            Y = T;
        }
        // return to single precision
        for( int i = 0; i < n; i++ )
            dst[i] = float2( (float)X[i].x, (float)X[i].y );
        src += n;
        dst += n;
    }
    delete [] X;
    delete [] Y;
}

void computeIFFTReference( float2 *dst, float2 *src, int n, int batch )
{   
    double2 *X = new double2[n];
    double2 *Y = new double2[n];
    for( int ibatch = 0; ibatch < batch; ibatch++ )
    {
        // go to double precision
        for( int i = 0; i < n; i++ )
            X[i] = double2( src[i].x, src[i].y );

        // FFT in double precision
        for( int kmax = 1, jmax = n/2; kmax < n; kmax *= 2, jmax /= 2 )
        {
            for( int k = 0; k < kmax; k++ )
            {
                double phi = 2.0 * M_PI * k /(2.0 * kmax);
                double2 w = double2( cos(phi), sin(phi) ); 
                for( int j = 0; j < jmax; j++ )
                {
                    Y[j*2*kmax + k]        = X[j*kmax + k] + w * X[j*kmax + n/2 + k];
                    Y[j*2*kmax + kmax + k] = X[j*kmax + k] - w * X[j*kmax + n/2 + k];
                }
            }
            double2 *T = X;
            X = Y;
            Y = T;
        }
        // return to single precision
        for( int i = 0; i < n; i++ )
            dst[i] = float2( (float)X[i].x / n, (float)X[i].y / n);
        src += n;
        dst += n;
    }
    delete [] X;
    delete [] Y;
}


const float ulp =  1.192092896e-07f;
inline float norm2( float2 a ) { return a.x*a.x+a.y*a.y ; }
inline float2 operator-( float2 a, float2 b ) { return float2( a.x - b.x, a.y - b.y ); }
//inline float max( float a, float b ) { return a > b ? a : b; }

//  
//	The relative forward error is bound by ~logN, see Th. 24.2 in:
//  
//  Higham, N. J. 2002. Accuracy and Stability of Numerical Algorithms, SIAM.
//    (Available online at http://books.google.com/books?id=epilvM5MMxwC)
//  
float relative_error( float2 *reference, float2 *result, int n, int batch )
{   
    float error = 0;
    for( int i = 0; i < batch; i++ )
    {
        float diff = 0, norm = 0;
        for( int j = 0; j < n; j++ )
        {
            diff += norm2( reference[j] - result[j] );
            norm += norm2( reference[j] );
        }
        if( _isnan( diff ) )
            return -1;
        
        error = max( error, diff / norm );
        
        reference += n;
        result += n;
    }
    return sqrt( error ) / ulp;
}   

void verifyData(float2 *d, float2 *r, int n, int batch)
{
	float ulps = relative_error(r,d,n/batch,batch);
	printf("error = %.1lf \n", ulps);
}

#else
void computeFFTReference( float2 *dst, float2 *src, int n, int batch ) {;}
void computeIFFTReference( float2 *dst, float2 *src, int n, int batch ) {;}
#define verifyData(d,r,n,b)

#endif


void printData(float2* pData, unsigned int size, unsigned int batchSize) 
{
#if VERBOSE=1
	for (unsigned int i = 0; i < size; i++)
    {
        printf("%.2f:%.2f  ", pData->x, pData->y);
        pData++;
        if (i % batchSize == batchSize - 1)
        {
            printf("\n");
        }
    }
#endif
}

void printData(float* pData, unsigned int size, unsigned int batchSize) 
{
    for (unsigned int i = 0; i < size; i++)
    {
        printf("%.2f  ", *pData);
        pData++;
        if (i % batchSize == batchSize - 1)
        {
            printf("\n");
        }
    }
}

void printData(float4* pData, unsigned int size, unsigned int batchSize) 
{
    for (unsigned int i = 0; i < size; i++)
    {
        printf("%.2f:%.2f:%.2f:%.2f  ", pData->x, pData->y, pData->z, pData->w);
        pData++;
        if (i % batchSize == batchSize - 1)
        {
            printf("\n");
        }
    }
}

void testReferenece()
{
    unsigned int n = 512;
    float2 *origin = new float2[n];
    generateTestData(origin, n, n);
    printData(origin, n, n);

    float2 *data = new float2[n];
    computeFFTReference(data, origin, n, 1);
    computeIFFTReference(data, data, n, 1);

    printData(data, n, n);

    delete [] origin;
}



float *generate_pinned_buffer(unsigned int buffer_size, float **mem_handle, unsigned int iteration) {

    float *result_data = NULL;
    float *target_data = NULL;
    const unsigned int split_num = iteration*2; //every iteration uses 2 input buffers, 2 output buffers

#ifdef ATI_OS_VISTA
    unsigned long tempsize = buffer_size + 4096*split_num;
    unsigned int mask = (4096*split_num-1);
#else
    unsigned long tempsize = buffer_size+ 256*split_num;
    unsigned int mask = (256*split_num-1);
#endif

    result_data = (float*)malloc(tempsize);

#ifdef ATI_OS_VISTA
    target_data = (float*)(((uintptr_t)result_data +mask ) & (~mask));
#else
    target_data = (float*)(((uintptr_t)result_data + mask) & (~mask));
#endif

    *mem_handle = result_data;
    return target_data;
}



bool testDataTrans(TomoFFT& tomoFFT) {

    Timer timer;
    DataTransManager *dataManager = tomoFFT._dataMgr;
    Device *gpu_device = Runtime::getInstance()->getDeviceManager()->getDefaultDevice();

    //Setup input
    float *input_handle;
    const unsigned int width = 512;
    const unsigned int height = 512*64;
    const unsigned int input_size = width*height*sizeof(float);
    float *pinned_input = generate_pinned_buffer(input_size, &input_handle, 1);
    for(int i=0; i<height; i++) {
        for(int j=0; j<width; j++) {
            pinned_input[i*width+j] = (float)(j) + ((float)i)/height;//(float)(i*width+j);
        }
    }

    //Setup output
    float *output_handle;
    const unsigned int ouput_size = width * height * sizeof(float);
    float *pinned_output = generate_pinned_buffer(ouput_size, &output_handle, 1);
    memset(pinned_output, 0, ouput_size);

    if( !dataManager->beginGPUCompute(gpu_device) ) {
        printf("Error GPU begin compute failed\n");
        return false;
    }

    
    //if(!dataManager->transferToGPU_CS(gpu_device, pinned_input, width, height)) {
    if(!dataManager->transferToGPU_PS(gpu_device, pinned_input, width, height)) { 
        printf("Error when transfer data to GPU\n");
        return false;
    }

    /*
    asynchronized copying, you can do something here
    */

    LocalBuffer *gbuf = dataManager->getGPUGlobalDataBuffer(gpu_device);
   
    //gbuf->writePinnedData(pinned_output, ouput_size);
    
    if( !dataManager->transferFromGPU_PS(gpu_device, pinned_output) ){
        printf("Error GPU transfer back failed\n");
        return false;
    }

    /*
    you can do something here by binding gbuf to your Kernel program,
    Then, you can call transferToGPU and getGPUGlobalDataBuffer again
    */

    if( !dataManager->endGPUCompute(gpu_device) ) {
        printf("Error GPU end compute failed\n");
        return false;
    }

    debug_info(pinned_output);

    delete input_handle;
    delete output_handle;
    delete dataManager;
    return true;
}


bool testFFT8(TomoFFT& tomoFFT, unsigned int n)
{
	float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
    generateTestData(data, n, 8);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    computeFFTReference(result, data, 8, n/8);
    //printData(result, n, 8);


    if(!tomoFFT.FFT8(data, n))
    { 
        printf("Failed to run FFT8!\n");
        return false;
    }
    printData(data, n, 8);
	verifyData(data,result,n, n/8);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

bool testFFT16(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    generateTestData(data, n, 16);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    computeFFTReference(result, data, 16, n/16);
    printData(result, n, 16);

    if(!tomoFFT.FFT16(data, n))
    { 
        printf("Failed to run FFT16!\n");
        return false;
    }
    printData(data, n, 16);
	verifyData(data,result,n, n/16);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

bool testFFT32(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    generateTestData(data, n, 32);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    computeFFTReference(result, data, 32, n/32);
    printData(result, n, 32);

    if(!tomoFFT.FFT32(data, n))
    { 
        printf("Failed to run FFT32!\n");
        return false;
    }
    printData(data, n, 32);
	verifyData(data,result,n, n/32);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

bool testFFT64(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    generateTestData(data, n, 64);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    computeFFTReference(result, data, 64, n/64);
    printData(result, n, 8);

    if(!tomoFFT.FFT64(data, n))
    { 
        printf("Failed to run FFT64!\n");
        return false;
    }
    printData(data, n, 8);
	verifyData(data,result,n, n/64);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

bool testFFT256(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    generateTestData(data, n, 256);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);
    computeFFTReference(result, data, 256, n/256);
    printData(result, n, 8);

    if(!tomoFFT.FFT256(data, n))
    { 
        printf("Failed to run FFT256!\n");
        return false;
    }
    printData(data, n, 8);
	verifyData(data,result,n, n/256);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

bool testFFT512(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
//    generateTestData(data, n, 512);
	float2 * pData = data;
    for (unsigned int i = 0; i < n/512; i++)
    {
		for (int j=0; j<8; j++)
			for (int k=0; k<64; k++)
				pData[k + 64 * j] = float2(k, j);
        //pData->x = (rand()/(float)RAND_MAX)*2-1;
        //pData->y = (rand()/(float)RAND_MAX)*2-1;
        pData += 512;
    }
	
	
	float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
    computeFFTReference(result, data, 512, n/512);
    printData(data, 512, 64);

    if(!tomoFFT.FFT512(data, n))
    { 
        printf("Failed to run FFT512!\n");
        return false;
    }
    printData(data, 512, 64);
	verifyData(data,result,n, n/512);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}


bool testFFT1024(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
    generateTestData(data, n, 1024);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
    computeFFTReference(result, data, 1024, n/1024);
    printData(result, n, 32);

    if(!tomoFFT.FFT1024(data, n))
    { 
        printf("Failed to run FFT1024!\n");
        return false;
    }
    printData(data, n, 32);
	verifyData(data,result,n, n/1024);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

//bool computeFFT2048(float2 * result, float2 * data)
//{
//	int i,j;
//	float2 inter[2048];
//	for (i=0; i<256; i++) {
//		float2 tmp[8], tmpr[8];
//		for (j=0; j<8; j++) {
//			tmp[j] = data[j * 256 + i];
//		}
//		computeFFTReference(tmpr, tmp, 8, 1);
//		for (j=0; j<8; j++) {
//			float phi = -2.0 * M_PI * i * j /(2048.0);
//			result[j * 256 + i] = tmpr[j];// * float2(cosf(phi), sinf(phi));
//		}
//	}
// 
//	//computeFFTReference(inter, result, 256, 8);
//
//	//for (i=0; i<256; i++) {
//	//	for (j=0; j<8; j++) {
//	//		result[j + 8 * i] = inter[j * 256 + i];
//	//	}
//	//}
//	return true;
//}

bool testFFT2048(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
    generateTestData(data, n, 2048);
    float2 *result = (float2 *)_aligned_malloc(sizeof(float2) * n, 4096);	
    computeFFTReference(result, data, 2048, n/2048);
    printData(result, n, 16);

    if(!tomoFFT.FFT2048(data, n))
    { 
        printf("Failed to run FFT2048!\n");
        return false;
    }
    printData(data, n, 16);
	verifyData(data,result,n, n/2048);

    _aligned_free(data);
    _aligned_free(result);

    return true;
}

bool testIFFT8(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 2);
    float2 *result = new float2[n];
    computeIFFTReference(result, data, 8, n/8);
    printData(result, n, 8);

    if(!tomoFFT.IFFT8(data, n))
    { 
        printf("Failed to run IFFT8!\n");
        return false;
    }
    printData(data, n, 8);

    delete [] data;
    delete [] result;

    return true;
}

bool testIFFT16(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 16);
    float2 *result = new float2[n];
    computeIFFTReference(result, data, 16, n/16);
    printData(result, n, 16);

    if(!tomoFFT.IFFT16(data, n))
    { 
        printf("Failed to run IFFT16!\n");
        return false;
    }
    printData(data, n, 16);

    delete [] data;
    delete [] result;

    return true;
}

bool testIFFT64(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 64);
    float2 *result = new float2[n];
    computeIFFTReference(result, data, 64, n/64);
    printData(result, n, 64);

    if(!tomoFFT.IFFT64(data, n))
    { 
        printf("Failed to run IFFT64!\n");
        return false;
    }
    printData(data, n, 64);

    delete [] data;
    delete [] result;

    return true;
}

bool testIFFT512(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 512);
    float2 *result = new float2[n];
    computeIFFTReference(result, data, 512, n/512);
    printData(result, n, 512);

    if(!tomoFFT.IFFT512(data, n))
    { 
        printf("Failed to run IFFT512!\n");
        return false;
    }
    printData(data, n, 512);

    delete [] data;
    delete [] result;

    return true;
}


bool testIFFT1024(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 3);
    float2 *result = new float2[n];
    computeIFFTReference(result, data, 1024, n/1024);
    printData(result, n, 1024);

    if(!tomoFFT.IFFT1024(data, n))
    { 
        printf("Failed to run IFFT1024!\n");
        return false;
    }
    printData(data, n, 1024);
	verifyData(data,result,n, n/1024);

    delete [] data;
    delete [] result;

    return true;
}

bool testIFFT2048(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 2048);
    float2 *result = new float2[n];
    computeIFFTReference(result, data, 2048, n/2048);
    printData(result, n, 512);

    if(!tomoFFT.IFFT2048(data, n))
    { 
        printf("Failed to run IFFT2048!\n");
        return false;
    }
    printData(data, n, 512);
	verifyData(data,result,n, n/2048);

    delete [] data;
    delete [] result;

    return true;
}


bool testFFT8_IFFT8(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 8);
    printData(data, n, 8);

    if(!tomoFFT.FFT8(data, n))
    { 
        printf("Failed to run FFT8!\n");
        return false;
    }
    if(!tomoFFT.IFFT8(data, n))
    { 
        printf("Failed to run IFFT8!\n");
        return false;
    }
    printData(data, n, 8);

    delete [] data;
    return true;
}

bool testFFT16_IFFT16(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 16);
    printData(data, n, 16);

    if(!tomoFFT.FFT16(data, n))
    { 
        printf("Failed to run FFT16!\n");
        return false;
    }
    if(!tomoFFT.IFFT16(data, n))
    { 
        printf("Failed to run IFFT16!\n");
        return false;
    }
    printData(data, n, 16);

    delete [] data;
    return true;
}

bool testFFT64_IFFT64(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 64);
    printData(data, n, 64);

    if(!tomoFFT.FFT64(data, n))
    { 
        printf("Failed to run FFT64!\n");
        return false;
    }
    if(!tomoFFT.IFFT64(data, n))
    { 
        printf("Failed to run IFFT64!\n");
        return false;
    }
    printData(data, n, 64);

    delete [] data;
    return true;
}

bool testFFT512_IFFT512(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 512);
    printData(data, n, 512);

    if(!tomoFFT.FFT512(data, n))
    { 
        printf("Failed to run FFT512!\n");
        return false;
    }
    if(!tomoFFT.IFFT512(data, n))
    { 
        printf("Failed to run IFFT512!\n");
        return false;
    }
    printData(data, n, 512);

    delete [] data;
    return true;
}

bool testFFT1024_IFFT1024(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 1024);
    printData(data, n, 1024);

    if(!tomoFFT.FFT1024(data, n))
    { 
        printf("Failed to run FFT1024!\n");
        return false;
    }
    if(!tomoFFT.IFFT1024(data, n))
    { 
        printf("Failed to run IFFT1024!\n");
        return false;
    }
    printData(data, n, 1024);

    delete [] data;
    return true;
}

bool testFFT2048_IFFT2048(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 2048);
    printData(data, n, 512);

    if(!tomoFFT.FFT2048(data, n))
    { 
        printf("Failed to run FFT2048!\n");
        return false;
    }
    if(!tomoFFT.IFFT2048(data, n))
    { 
        printf("Failed to run IFFT2048!\n");
        return false;
    }
    printData(data, n, 512);

    delete [] data;
    return true;
}

bool testFFT2048_IFFT2048_NT(TomoFFT& tomoFFT, unsigned int n)
{
    float2 *data = new float2[n];
    generateTestData(data, n, 2048);
    printData(data, n, 512);

    if(!tomoFFT.FFT2048NT(data, n))
    { 
        printf("Failed to run FFT2048!\n");
        return false;
    }
    if(!tomoFFT.IFFT2048NT(data, n))
    { 
        printf("Failed to run IFFT2048!\n");
        return false;
    }
    printData(data, n, 512);

    delete [] data;
    return true;
}


bool test360MData(TomoFFT& tomoFFT) 
{
    DataTransManager *dataManager = tomoFFT._dataMgr;
    Device *gpu_device = Runtime::getInstance()->getDeviceManager()->getDefaultDevice();

    //Setpu input
    const unsigned int iteration = 4;
    const unsigned int projection_i = 90;
    
    const unsigned int width = 512;
    const unsigned int height = iteration*512*projection_i;
 
    float *input_handle;
    const unsigned int input_size = width*height*sizeof(float);
    float *pinned_input = generate_pinned_buffer(input_size, &input_handle, iteration);

    for(int i=0; i<height; i++) {
        for(int j=0; j<width; j++) {
            pinned_input[i*width+j] = (float)(j);//+ ((float)i)/height;;//(i*width+j);// //(float)(i*width+j);
        }
    }

    
    //FILE *input_file;
    //fopen_s(&input_file,"E:\\App_work\\data\\FFT\\priData\\S-H-3D-100mm.prj","rb");
    //fread(pinned_input,sizeof(float),width*height,input_file);
    //fclose(input_file);
    //debug_info(pinned_input);
    

    //Setpu output
    float *output_handle;
    const unsigned int ouput_size = input_size;
    float *pinned_output = generate_pinned_buffer(ouput_size, &output_handle, iteration);
    memset(pinned_output, 0, ouput_size);

    float *input_start = pinned_input;
    float *ouput_start = pinned_output;
    const unsigned int i_height = height/iteration;
    const unsigned int i_step = width*i_height;

    if( !dataManager->beginGPUCompute(gpu_device) ) {
        printf("Error GPU begin compute failed\n");
        return false;
    }
    

    dataManager->initGPUMemory(gpu_device, width, i_height);
    
    ConstBuffer *conbuf = dataManager->getGPUConstBuffer(gpu_device);
    LocalBuffer *gbuf = dataManager->getGPUGlobalDataBuffer(gpu_device);
    tomoFFT.bindBuffer2048(gbuf, conbuf);

    Timer timer;
    timer.start();

    for(int i=0; i<iteration; i++) {

        if(!dataManager->transferToGPU_CS(gpu_device, input_start, width, i_height)) {
         printf("Error when transfer data to GPU\n");
         return false;
        }

        //tomoFFT.compute2048_5step(gbuf, i_height);
        tomoFFT.compute2048(i_height);
        /*
        else
            tomoFFT.simplecompute(i_height);
        */
        //gbuf->writePinnedData(ouput_start, ouput_size/iteration);
        
        if( !dataManager->transferFromGPU_PS(gpu_device, ouput_start) ){
            printf("Error GPU transfer back failed\n");
            return false;
        }

        input_start += i_step;
        ouput_start += i_step;
        //Sleep(10000);
    }

     timer.stop();
     printf("[ Total Time: %lf s ]\n", timer.getElapsedTime());

    if( !dataManager->endGPUCompute(gpu_device) ) {
        printf("Error GPU end compute failed\n");
        return false;
    }

    //debug_info(pinned_output);

    /*
    FILE *std_file;
    fopen_s(&std_file,"E:\\App_work\\data\\FFT\\priData\\cpu_fft.prj","rb");
    fread(pinned_input,sizeof(float), width*height, std_file);
    fclose(std_file);
    //debug_info(pinned_input);
    */

    /*FILE *output_file;
    fopen_s(&output_file, "E:\\App_work\\data\\FFT\\priData\\S-H-3D-100mm.result", "wb");
    fwrite(pinned_output,sizeof(float),width*height,output_file);
    fclose(output_file);
	*/
    /*
    perror("Comparing data errors...\n");
    ErrorChecker checker;
    checker.check_data(pinned_input, pinned_output, width*height);
    perror("Out data errors...\n");
    checker.dump_error_p();
    */
    

    delete input_handle;
    delete output_handle;
    delete dataManager;
    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
#if VERIFY
	int n_entries = 1024*64;
#else
	int n_entries = 1024*1024*64;
#endif
    int n_bytes = n_entries * sizeof(float2);

    TomoFFT tomoFFT;
    tomoFFT.initialize(0);

    //testReferenece();
    //test360MData(tomoFFT);
    //testDataTrans(tomoFFT);
    //testThroughput(tomoFFT);
    //testOutput(tomoFFT, n_entries);

//=======================
// Only functions in this block are garenteened to work
    //testFFT8(tomoFFT, n_entries);
    //testFFT16(tomoFFT, n_entries);
    //testFFT32(tomoFFT, n_entries);
	//testFFT64(tomoFFT, n_entries);
    testFFT256(tomoFFT, n_entries);
    testFFT512(tomoFFT, n_entries);
    testFFT1024(tomoFFT, n_entries);
    //testFFT2048(tomoFFT, n_entries);
//=======================

    //testIFFT8(tomoFFT, n_entries);
    //testIFFT16(tomoFFT, n_entries);
    //testIFFT64(tomoFFT, n_entries);
    //testIFFT512(tomoFFT, n_entries);
//    testIFFT1024(tomoFFT, n_entries);
//    testIFFT2048(tomoFFT, n_entries);

    //testFFT8_IFFT8(tomoFFT, n_entries);
    //testFFT16_IFFT16(tomoFFT, n_entries);
    //testFFT64_IFFT64(tomoFFT, n_entries);
    //testFFT512_IFFT512(tomoFFT, n_entries);
    //testFFT1024_IFFT1024(tomoFFT, n_entries);
    //testFFT2048_IFFT2048(tomoFFT, n_entries);
    //testFFT2048_IFFT2048_NT(tomoFFT, n_entries);

    return 0;
}
